package cn.doitedu.core;

import com.huaban.analysis.jieba.JiebaSegmenter;
import com.huaban.analysis.jieba.SegToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 数据管理service
 */
@Slf4j
public class DataManagerService {

    String dataFilePath;
    String metaFilePath;
    HashMap<String, List<Integer>> indexMap = new HashMap<>();
    JiebaSegmenter jiebaSegmenter = new JiebaSegmenter();
    int lineNumber = 0;
    //这句话不用写，可以利用lombok的@Slf4j自动注入
    // Logger log = LoggerFactory.getLogger(DataManagerService.class);

    public DataManagerService(String dataFilePath,String metaFilePath) throws Exception {
        log.debug("进入了DataManagerService的构造方法......");

        this.dataFilePath = dataFilePath;
        this.metaFilePath = metaFilePath;

        File file = new File(metaFilePath);
        if (file.exists()) {

            log.info("找到元数据文件:{}，文件size:{}，准备加载",metaFilePath,file.length());
            // 加载此前保存的元数据（索引hashmap，文件最大行号）
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            MetaData metaData = (MetaData) ois.readObject();
            log.info("找到元数据文件，加载成功");

            this.indexMap =  metaData.getIndexMap();
            this.lineNumber = metaData.getLineNumber();
        }else{

            log.warn("未找到元数据文件，准备初始化创建兴趣词分词索引");

            // 调用初始化索引的方法，来创建数据的索引
            initializeDataIndex(dataFilePath);

            log.warn("兴趣词分词索引，创建完成");
        }
    }

    /**
     * 初始化索引
     * @param dataFilePath
     * @throws IOException
     */
    public void initializeDataIndex(String dataFilePath) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(this.dataFilePath)));
        String line = "";


        while((line=bufferedReader.readLine())!=null){

            // 如果遇到文件末尾的空白行，则直接退出循环
            if (StringUtils.isBlank(line)) {
                break;
            }
            // 切字段，提取 兴趣词
            // 春风吹战鼓擂这个社会谁怕谁|我爱喝咖啡配巧克力|燕子飞了还有再回来的时候
            String interestWords = line.split(",")[5];

            // 分词
            List<SegToken> tokenList = jiebaSegmenter.process(interestWords, JiebaSegmenter.SegMode.SEARCH);

            // 将分词器返回的SegToken元素列表，映射成   String元素列表，并过滤掉 长度<2的词
            // 春风 战鼓 这个 社会 喝咖啡 咖啡 巧克力 燕子 还有 回来 时候
            Set<String> wordList = tokenList.stream().map(segToken -> segToken.word).filter(x -> x.length() > 1).collect(Collectors.toSet());

            // 将词逐个放入索引map
            for (String word : wordList) {
                List<Integer> lineNumberList = indexMap.getOrDefault(word, new ArrayList<>());
                lineNumberList.add(lineNumber);
                indexMap.put(word,lineNumberList);
            }
            // 行号++
            lineNumber ++;

        }
    }

    /**
     * 根据id查询学员信息
     */
    public String findStudentById(int queryId) throws Exception {

        // 构建一个读取文件的工具
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(this.dataFilePath)));

        String line = "";

        // 循环，每次读一行
        while ((line = bufferedReader.readLine()) != null) {

            // 将读到的一行数据（字符串），按照,分割成多个字符串（字符串数组）
            String[] arr = line.split(",");
            // 取数组中的第0个，就是id字段
            String idStr = arr[0];
            // 将取出来的字符串类型的id值，解析成整数类型的id
            int idInt = Integer.parseInt(idStr);

            // 判断读到的这一行id，是否与查询目标要求的id相等
            if (idInt == queryId) {
                // 如果相等，则返回找到的整行数据
                bufferedReader.close();
                return line;
            }

        }
        bufferedReader.close();
        return null;

    }


    /**
     * 根据 name查询学员信息
     * 方法名： findStudentsByName
     */
    public List<String> findStudentsByName(String queryName) throws Exception {
        // 构建一个读文件的工具
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(this.dataFilePath)));
        String line;
        ArrayList<String> studentList = new ArrayList<>();
        while((line=bufferedReader.readLine())!=null){

            String[] arr = line.split(",");
            // 提取姓名
            String name = arr[1];
            if(queryName.equals(name)){
                // 把找到行，放入list集合
                studentList.add(line);
            }
        }

        bufferedReader.close();
        return studentList;
    }


    /**
     * 通用方法，可以根据任意指定的字段条件来查询学员信息
     *
     * findStudentsByCondition(0,"3")
     * findStudentsByCondition(1,"刘洋")
     * findStudentsByCondition(2,"18")
     *
     */
    public List<String> findStudentsByCondition(int fieldIndex, String queryValue) throws IOException {

        // 构建一个读文件的工具
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(this.dataFilePath)));
        String line;
        ArrayList<String> studentList = new ArrayList<>();
        while((line=bufferedReader.readLine())!=null){

            String[] arr = line.split(",");
            String fieldValue = arr[fieldIndex];
            if(fieldValue.equals(queryValue)){
                studentList.add(line);
            }
        }
        return studentList;
    }


    /**
     * 根据兴趣词查学员信息
     * 要求利用到索引
     */
    public List<String> findStudentsByInterestWord(String queryWord) throws IOException {

        // 创建一个list，用来装查询到的结果
        ArrayList<String> studentList = new ArrayList<>();


        // 先对查询条件词，做分词
        List<SegToken> queryTokens = jiebaSegmenter.process(queryWord, JiebaSegmenter.SegMode.SEARCH);

        // 速溶  咖啡
        ArrayList<String> queryWordList = new ArrayList<>();

        for (SegToken queryToken : queryTokens) {
            String word = queryToken.word;
            if(word.length()>1){
                queryWordList.add(word);
            }
        }

        ArrayList<Integer> lineNumberList = new ArrayList<>();

        for (String wd : queryWordList) {
            // 先从索引中查找哪些行信息中包含当前遍历到的条件词
            List<Integer> tmpList = indexMap.getOrDefault(wd,new ArrayList<>());

            // 将本词索引到的所有行号，添加到总的 行号list 中
            lineNumberList.addAll(tmpList);

        }


        if(lineNumberList.isEmpty()){
            return studentList;
        }else{

            // 构建一个读文件的工具
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(this.dataFilePath)));
            String line;


            int currentLineNumber = 0;
            while( (line = bufferedReader.readLine())!=null  ){

                if(lineNumberList.contains(currentLineNumber)){
                    studentList.add(line);

                    // 如果查询到的结果的总行数 == 索引中查询出来的包含指定词的总条数，可以提前中止循环
                    if(studentList.size() == lineNumberList.size()){
                        break;
                    }

                }

                // 当前行号递增
                currentLineNumber++;

            }

            bufferedReader.close();

        }

        // 返回结果
        return studentList;
    }


    /**
     * 新增 学员信息
     */
    public void appendStudent( StudentBean studentBean ) throws Exception {

        StringBuilder sb = new StringBuilder();
        // 将接收到的学员信息，拼接成文件要求的格式
        sb.append(studentBean.getId()).append(",")
                .append(studentBean.getName()).append(",")
                .append(studentBean.getAge()).append(",")
                .append(studentBean.getGender()).append(",")
                .append(studentBean.getSchool()).append(",")
                .append(studentBean.getInterestWords()).append("\r\n");

        // 将接收到的信息，写入数据文件
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dataFilePath,true)));
        bw.write(sb.toString());
        bw.close();

        // 更新索引
        String interestWords = studentBean.getInterestWords();
        List<SegToken> tokenList = jiebaSegmenter.process(interestWords, JiebaSegmenter.SegMode.SEARCH);
        Set<String> wordSet = tokenList.stream().map(token -> token.word).filter(token -> token.length() > 1).collect(Collectors.toSet());

        for (String wd : wordSet) {
            List<Integer> lineNumberList = indexMap.getOrDefault(wd, new ArrayList<>());
            // 将当前新行的行号  ，添加到行号列表中
            lineNumberList.add(lineNumber);
            // 将更新好的行号列表，重新放回索引map
            indexMap.put(wd,lineNumberList);
        }
        // 更新当前行号
        lineNumber++;
    }


    /**
     * 系统退出
     */
    public void exit() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(this.metaFilePath));

        MetaData metaData = new MetaData(this.indexMap, this.lineNumber);

        oos.writeObject(metaData);
        oos.close();

    }

}
